﻿/* 
 * Evo
 * 
 * Marcin Krupowicz (marcin@krupowicz.eu)
 * Barbara Szymanek (basia.szymanek@gmail.com)
 * 
 * January, 2009
 *
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using Evo.Operators;
using System.IO;


namespace Evo
{
	/// <summary>
	/// Standard evolutionary algorithm. Requires evolutionary operators to work.
	/// </summary>
	/// <typeparam name="T">Type of individual</typeparam>
	public class EvolutionaryAlgorithm<T> : IAlgorithm where T: IIndividual
    {
        #region fields
        protected int populationSize;
        protected int maxIteration;
        protected int chromosomeLength;

        protected Population<T> population;
        protected List<IOperator<T>> operators;
        protected EvaluateChromosomeDelegate objectiveFunction;
        protected ScalingType fitnessScaling = ScalingType.None;
		protected bool verbose;
		protected TextWriter verboseOutput;
        #endregion

		#region properties
		/// <summary>
		/// Get/set evolving population
		/// </summary>
		public Population<T> Population
		{
			get { return population; }
			set { population = value; }
		}

		/// <summary>
		/// Get maximum number of interations to be processed
		/// </summary>
		public int MaxIteration
		{
			get { return maxIteration; }
		}

		/// <summary>
		/// Get length of chromosome of each individual
		/// </summary>
		public int ChromosomeLength
		{
			get { return chromosomeLength; }
		}

		/// <summary>
		/// Get/set list of evoluationary operators
		/// </summary>
		public List<IOperator<T>> Operators
		{
			get { return operators; }
			set { operators = value; }
		}

		/// <summary>
		/// Get/set objective function which will be *maximized*
		/// </summary>
		public EvaluateChromosomeDelegate ObjectiveFunction
		{
			get { return objectiveFunction; }
			set { objectiveFunction = value; }
		}

		/// <summary>
		/// Get/set type of scaling fitness values
		/// </summary>
		public ScalingType ScalingType
		{
			get { return fitnessScaling; }
			set { fitnessScaling = value; }
		}

		/// <summary>
		/// Get/set verbose mode
		/// </summary>
		public bool Verbose
		{
			get { return verbose; }
			set { verbose = value; }
		}

		/// <summary>
		/// Get/set output where algorithm should print information while working (related to Verbose property)
		/// </summary>
		public TextWriter VerboseOutput
		{
			get { return verboseOutput; }
			set { verboseOutput = value; }
		}
		#endregion

        #region contructors
		/// <summary>
		/// Create evolutionary algorithm
		/// </summary>
		/// <param name="populationSize">size of population to be evolved</param>
		/// <param name="chromosomeLength">length of chromosome of each individual</param>
		/// <param name="maxIteration">number of iterations to be performed</param>
		/// <param name="verbose">verbose mode</param>
        public EvolutionaryAlgorithm(int populationSize, int chromosomeLength, int maxIteration, bool verbose)
		{
            if (populationSize <= 0)
                throw new Exception("Size of population is <= 0");
            if (maxIteration <= 0)
                throw new Exception("Maximal iteration is <= 0");
            if (chromosomeLength <= 0)
                throw new Exception("Chromosome length is <= 0");

            this.populationSize = populationSize;
            this.maxIteration = maxIteration;
            this.chromosomeLength = chromosomeLength;
			this.verbose = verbose;

            operators = new List<IOperator<T>>();
        }

        #endregion


		#region private methods
		protected virtual void doIteration()
		{
			foreach (IOperator<T> op in operators)
			{
				population = op.Execute(population);
				population.Evaluate(fitnessScaling);        //oblicza tylko fitness!!
			}
		}

		protected virtual void OutputStatistic(int iteration)
		{
			String thisIterationStatistic = String.Format(
				System.Globalization.CultureInfo.InvariantCulture.NumberFormat,
				"{0}, {1}, {2}, {3}, {4}",
				iteration, Math.Abs(population.Best.Value), Math.Abs(population.Worst.Value), Math.Abs(population.Average), population.StandardDeviation);

			verboseOutput.WriteLine(thisIterationStatistic);
		}
		#endregion


		#region methods
		/// <summary>
		/// Add new operator
		/// </summary>
		/// <param name="newOperator"></param>
		public virtual void AddOperator(IOperator<T> newOperator)
        {
            operators.Add(newOperator);
        }

		/// <summary>
		/// Print on VerboseOuput information about algorithm and operators.
		/// </summary>
        public void IntroduceYourself()
        {
            if (verboseOutput != null && verbose)
            {
                VerboseOutput.WriteLine(String.Format("Population size:\t{0}", this.populationSize));
                VerboseOutput.WriteLine(String.Format("Max iteration:\t{0}\n", this.maxIteration));
                
                foreach (IOperator<T> op in operators)
                    VerboseOutput.WriteLine(op.ToString());

            }

        }

		/// <summary>
		/// Run evolutionary algorithm
		/// </summary>
        public virtual void RunAlgorithm()
        {
            if (objectiveFunction == null)
                throw new Exception("Objective function is not assign!");

            if (operators.Count == 0)
                throw new Exception("There is no operators to execute!");
            
            if (population == null)
                population = Population<T>.GenerateRandom(populationSize, chromosomeLength);

            population.ObjectiveFunction = this.objectiveFunction;

            int iterationCounter = 0;

			population.Evaluate(fitnessScaling);
            while (iterationCounter < maxIteration)
            {
                doIteration();
                
                if(verboseOutput != null && verbose)
                    OutputStatistic(iterationCounter);
                
                iterationCounter++;
            }
        }
        #endregion

    }
}
